package gam.n1.areas.impl.a;

import com.google.common.collect.Sets;
import com.google.gson.JsonObject;
import gam.n1.MathHelper;
import gam.n1.achievement.n.DoubleAchievement;
import gam.n1.achievement.n.FloatAchievement;
import gam.n1.achievement.n.IntegerAchievement;
import gam.n1.areas.Area;
import gam.n1.areas.impl.SkillPlayBukkitArea;
import gam.n1.ask.AskEntity;
import gam.n1.ask.AskLocation;
import gam.n1.bc.BroadCastManager;
import gam.n1.conf.ConfManager;
import gam.n1.e.a.AreaSpawnEntityEvent;
import gam.n1.ent.AEntity;
import gam.n1.ent.AEntityManager;
import gam.n1.ent.i.AttackEntity;
import gam.n1.ent.i.DefendVEntity;
import gam.n1.ent.i.ProtectedEntity;
import gam.n1.ent.i.entitys.AbstractVillager;
import gam.n1.ent.i.entitys.AbstractZombie;
import gam.n1.ins.i.DefendVInsManager;
import gam.n1.ins.i.TestIns;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_16_R3.entity.CraftEntity;
import org.bukkit.entity.*;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;

public class DefaultDefendVArea extends SkillPlayBukkitArea implements DefendVArea {
    private Set<Location> playerRespawn; //场地玩家的出生点
    private Set<Location> monsterRespawn; //怪物出生点
    private Set<Location> protectedRespawn; //被保卫实体出生点
    private Set<ProtectedEntity> ptyps; //被保卫的实体
    private Set<AttackEntity> mtyps; //进攻的实体

    public DefaultDefendVArea(@NotNull Plugin plugin, @NotNull String key) throws Exception {
        super(plugin, key);
        this.be = new DefendVEvent(this);
        this.ic = new DefendVInsManager(this);
        getAchievementManager().addRule(new IntegerAchievement(()->0,"Level")); //玩家个人等级
        getAchievementManager().addRule(new DoubleAchievement(()->0d,"Damage")); //玩家伤害总量
        getAchievementManager().addRule(new IntegerAchievement(()->0,"Kill")); //玩家击杀总数
        getAchievementManager().addRule(new FloatAchievement(()->0f,"Exp"));//玩家获取总经验值
        setTime(30000);

    }

    @Override
    public List<LivingEntity> getProtected() {
        return getEntitys().stream().filter(e -> e instanceof LivingEntity).map(entity -> ((LivingEntity)entity)).collect(Collectors.toList());
    }


    @Override
    public boolean cI() {
        try {
            this.instances(Collections.singletonList(new TestIns(10000)));
        }catch (Exception ignored){}
        return super.cI() && playerRespawn != null && monsterRespawn != null && protectedRespawn != null
                && playerRespawn.size() > 0 && monsterRespawn.size() > 0 && protectedRespawn.size() > 0
                && ptyps != null && mtyps != null && ptyps.size() > 0 && mtyps.size() > 0;
    }


    @Override
    public void afterAction() {
        super.afterAction();
        Bukkit.getPluginManager().registerEvents(be,plugin());
        System.out.println("Area AttackEntity has : " + mtyps);
        System.out.println("Area ProtectEntity has : " + ptyps);
    }


    private Location q(int l){
        if (playerRespawn.size() > 0){
            Iterator<Location> _pl = playerRespawn.iterator();
            for (int i = 0; _pl.hasNext(); i++) {
                Location n = _pl.next();
                if (i == l) return n;
            }
        }
        return null;
    }


    @Override
    protected void rS() {
        super.rS();
        HandlerList.unregisterAll(be);
//        dieAll();
    }
    private void dieAll(){
        Bukkit.getScheduler().runTask(plugin(),() -> {
            refresh();
            getEntitys().stream().filter(e -> e instanceof Creature).forEach(entity -> ((Creature) entity).damage(((Creature) entity).getHealth()));
        });
    }
    @Override
    public boolean spawn(AEntity spawn,Location location) {
        Objects.requireNonNull(spawn);
        Objects.requireNonNull(location);
        Entity var1 = spawn.newAEntity();
        ((CraftEntity) var1).getHandle().setPositionRotation(
                location.getX(),
                location.getY(),
                location.getZ(),
                location.getYaw(),
                location.getPitch()
        );
        ((CraftWorld) getAreaInWorld()).getHandle().addEntity(((CraftEntity) var1).getHandle());
        return true;
    }

    @Override
    public boolean spawn() {
        Location var1 = null;
        AEntity var2 = null;
        int var3 = MathHelper.rl(random, monsterRespawn.size());
        int var4 = MathHelper.rl(random, mtyps.size()) ;

        Iterator<Location> var5 = monsterRespawn.iterator();
        int i = 0;
        while (var5.hasNext()) {
            if (++i == var3){
                var1 = var5.next();
                break;
            }
        }

        Iterator<AttackEntity> var6 = mtyps.iterator();
        i = 0;
        while (var6.hasNext()) {
            if (++i == var4){
                var2 = var6.next();
                break;
            }
        }
        if (var1 != null && var2 != null){
            System.out.println("go spawn!!");
            spawn(var2, var1);
            return true;
        }
        return false;
    }

    @Override
    public Area getHandler() {
        return this;
    }



    @Override
    public Set<Location> getPointAtRespawn() {
        return Collections.unmodifiableSet(this.playerRespawn);
    }
    @Override
    public Area reborn(Set<Location> reborn)  {
        if (!isRunning()){
            if (playerRespawn == null){
                playerRespawn = new HashSet<>(reborn);
            } else playerRespawn.addAll(reborn);
        }
        return this;
    }
    @Override
    public Area toe(Set<AttackEntity> m) {
        if (!isRunning()){
            if (mtyps == null){
                mtyps = new HashSet<>(m);
            } else mtyps.addAll(m);
        }
        return this;
    }

    @Override
    public Area toa(Set<ProtectedEntity> j)  {
        if (!isRunning()){
            if (ptyps == null){
                ptyps = new HashSet<>(j);
            } else ptyps.addAll(j);
        }
        return this;
    }

    @Override
    public Area vol(Set<Location> vl) {
        if (!isRunning()){
            if (protectedRespawn == null){
                protectedRespawn = new HashSet<>(vl);
            } else protectedRespawn.addAll(vl);
        }
        return this;
    }

    @Override
    public Area por(Set<Location> rL){
        if (!isRunning()){
            if (monsterRespawn == null) {
                monsterRespawn = new HashSet<>(rL);
            } else monsterRespawn.addAll(rL);
        }
        return this;
    }
    public static class CanNotModifiedException extends Exception{
        public CanNotModifiedException(String message) {
            super(message);
        }
    }
    @Override
    public CompletableFuture<Area> aH(Player p) {
        if (nu()){
            return CompletableFuture
                    .supplyAsync(()->reborn(Sets.newHashSet(gP(p).apply(this))))
                    .thenApplyAsync(a -> vol(Sets.newHashSet(gP(p).apply(a))))
                    .thenApplyAsync(a -> por(Sets.newHashSet(gP(p).apply(a))))
                    .thenApplyAsync(a -> {
                        toe(Sets.newHashSet(new AbstractZombie(this,null)));
                        toa(Sets.newHashSet(new AbstractVillager(this)));
                        return a;
                    });
//                    .thenApplyAsync(a -> toa(Sets.newHashSet(gE(ProtectedEntity.class).apply(a))))
//                    .thenApplyAsync(a -> toe(Sets.newHashSet(gE(AttackEntity.class).apply(a))));
        }
        return super.aH(p)
                .thenApplyAsync(a -> reborn(Sets.newHashSet(gP(p).apply(a))))
                .thenApplyAsync(a -> vol(Sets.newHashSet(gP(p).apply(a))))
                .thenApplyAsync(a -> por(Sets.newHashSet(gP(p).apply(a))))
                .thenApplyAsync(a -> {
                    toe(Sets.newHashSet(new AbstractZombie(this,null)));
                    toa(Sets.newHashSet(new AbstractVillager(this)));
                    return a;
                });
//                .thenApplyAsync(a -> toa(Sets.newHashSet(gE(ProtectedEntity.class).apply(a))))
//                .thenApplyAsync(a -> toe(Sets.newHashSet(gE(AttackEntity.class).apply(a))));
    }
    @NotNull
    private Function<Area,Location> gP(Player p) {
        return area -> {
            ConfManager.Msg.send(p,ConfManager.Msg.getString("area.create.set.point"),null,null);
            AskLocation l = new AskLocation();
            Bukkit.getPluginManager().registerEvents(l,area.plugin());
            return l.get();
        };
    }
    @NotNull
    private <T extends AEntity<LivingEntity>> Function<Area,T> gE(Class<T> t) {
        return area -> {
            AskEntity e = new AskEntity();
            Bukkit.getPluginManager().registerEvents(e,area.plugin());
            Entity v = e.get();
            return t.isAssignableFrom(v.getClass()) ? (T) v : null;
        };
    }
    protected boolean nu(){
        return getBox() != null && getBox().isNotNull();
    }
    @Override
    public List<Entity> getMonsters() {
        return Collections.unmodifiableList(getEntitys().stream().filter(e -> e instanceof Creature).collect(Collectors.toList()));
    }

    @Override
    public Map<String, Object> save() {
        Map<String,Object> map = super.save();
        map.put("pl",js(playerRespawn));
        map.put("vl",js(protectedRespawn));
        map.put("ml",js(monsterRespawn));
        return map;
    }
    private Set<JsonObject> js(Set<Location> ls){
        Set<JsonObject> var2 = Sets.newHashSet();
        for (Location n : ls) {
            JsonObject jo = new JsonObject();
            jo.addProperty("world",n.getWorld().getName());
            jo.addProperty("x", n.getX());
            jo.addProperty("y", n.getY());
            jo.addProperty("z", n.getZ());
            var2.add(jo);
        }
        return var2;
    }

    @Override
    public boolean isMonsterInArea(LivingEntity monster) {
        return box.ie(monster);
    }
    protected boolean ivp(Entity entity){
        return entity instanceof LivingEntity && !(entity instanceof HumanEntity);
    }
}
